package ui;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.Map;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;

import searcher.Hit;
import searcher.HitSet;
import searcher.ISearcher;
import searcher.NaiveSearcher;
import searcher.NittySearcher;
import searcher.Request;
import util.CommonFactory;
import util.FileUtils;
import util.Summary;
import util.TermLabeler;
import util.ViewUtils;
import util.parser.IParser;
import util.parser.SMTHParser;
import algorithm.text.wordsegment.WordSegmentor;

public class GUI {
	public static final int RESULTS_PER_PAGE = 10;
	public static final int MAX_NAV_BUTTONS = 10;
	public static final int MAX_SENTENCE_LEN_FOR_SUMMARY = 160;
	
	public static final String TEMP_HTML_FILE_NAME = 
		System.getProperty("user.dir") + "/data/temp.html";
	public static final String SPLASH_HTML_FILE_NAME = 
		System.getProperty("user.dir") + "/data/template/splash.html";
	public static final URL SPLASH_URL; 
	
	public static final ISearcher searcher = new NittySearcher(CommonFactory.getReverseIndex());
	public static final IParser parser = new SMTHParser();
	public static final WordSegmentor ws = CommonFactory.getWordSegmentor(); 
	public static final Summary summary;
	public static final TermLabeler termLabeler;
	
	public static final String RESULT_TEMPLATE;
	public static final String SINGLE_RESULT_TEMPLATE;
	
	static {
		URL tu = null;
		try {
			tu = new URL("file:" + SPLASH_HTML_FILE_NAME);
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		SPLASH_URL = tu;
		
		RESULT_TEMPLATE = getTextFileContent("data/template/all.txt");
		SINGLE_RESULT_TEMPLATE = getTextFileContent("data/template/single.txt");
		summary = new Summary();
		summary.setSummaryLength(MAX_SENTENCE_LEN_FOR_SUMMARY);
		termLabeler = new TermLabeler();
        termLabeler.addDecor("<b><font color=\"blue\">", "</font></b>");        
	}
	
	public static HitSet getSearchResult(String query) {
		return searcher.getSearchResult(new Request(query));
	}
	
	public static class MainFrame extends JFrame {
		private JPanel statPanel;
		private JTextField queryTextField;
		private JEditorPane resultPane;
		private JButton searchButton;

		private Action searchAction;
		private Action switchPageAction;
		private Action switchBtnClickedAction;
		
		private int resultStartIndex;
		private HitSet hitSet;
		private String query;
		private long time;
		
		/**
		 * 构造MainFrame
		 * @param title
		 */
		public MainFrame(String title) {
			super(title);
			Container container = getContentPane();
			container.setLayout(new BorderLayout());
			// north
			JPanel northPanel = new JPanel();
			northPanel.setLayout(new BoxLayout(northPanel, BoxLayout.X_AXIS));
			queryTextField = new JTextField("Type in your query here :-)");
			queryTextField.setAlignmentY(CENTER_ALIGNMENT);
			queryTextField.setToolTipText("input query text here.");
			northPanel.add(queryTextField);
			searchButton = new JButton("Search");
			northPanel.add(searchButton);
			container.add(northPanel, BorderLayout.NORTH);
			// center
			resultPane = new JEditorPane();
			resultPane.setEditable(false);
			JScrollPane scroller = new JScrollPane();
			scroller.getViewport().add(resultPane);
			container.add(scroller, BorderLayout.CENTER);
			// south
			statPanel = new JPanel();
			statPanel.setLayout(new FlowLayout());
			statPanel.add(new JLabel("<<< Navigate Bar >>>"));
			container.add(statPanel, BorderLayout.SOUTH);
			// add listeners
			createListener();
		}
		
		private void createListener() {
			switchBtnClickedAction = new AbstractAction() {
				@Override
				public void actionPerformed(ActionEvent e) {
					JButton btn = (JButton) e.getSource();
					resultStartIndex = Integer.parseInt(btn.getName().substring(3));
					switchPageAction.actionPerformed(e);
				}
			};
			switchPageAction = new AbstractAction() {
				@Override
				public void actionPerformed(ActionEvent e) {
					updateResultPane(time, 
							ws.segmentToWords(query, 0, query.length(), 
									WordSegmentor.ALGORITHM_DYNAMIC));
					updateStatPanel();
				}
			};
			searchAction = new AbstractAction() {
				@Override
				public void actionPerformed(ActionEvent e) {
					query = queryTextField.getText();
					long start = System.currentTimeMillis();
					hitSet = getSearchResult(query);
					time = System.currentTimeMillis() - start;
					resultStartIndex = 0;
					switchPageAction.actionPerformed(e);
				}
			};
			searchButton.addActionListener(searchAction);
			queryTextField.addFocusListener(new FocusAdapter() {
				@Override
				public void focusGained(FocusEvent e) {
					queryTextField.selectAll();
				}
			});
			queryTextField.addActionListener(searchAction);
		}

		/**
		 * 更新最下方的NavPanel
		 * @param time 
		 * @param parsedQuery 
		 */
		public void updateResultPane(long time, List<String> parsedQuery) {
			List<Hit> hits = hitSet.getHits(resultStartIndex, RESULTS_PER_PAGE);
			generateHtmlFile(hitSet, resultStartIndex, parsedQuery, time);
			try {
				resultPane.setPage(SPLASH_URL);
				resultPane.setPage(new URL("file:" + TEMP_HTML_FILE_NAME));
			} catch (MalformedURLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		/**
		 * create a JButton
		 * @param text
		 * @param targetIndex
		 * @return
		 */
		private JButton createButton(String text, int targetIndex) {
			JButton btn = new JButton(text);
			btn.setName("PG_" + targetIndex);
			btn.addActionListener(switchBtnClickedAction);
			// target index is valid and current page is not in the target range
			btn.setEnabled(targetIndex >= 0 && targetIndex < hitSet.size() &&
					(resultStartIndex < targetIndex || 
					resultStartIndex >= targetIndex + RESULTS_PER_PAGE));
			return btn;
		}
		/**
		 * 更新最下方的NavPanel
		 */
		public void updateStatPanel() {
			statPanel.removeAll();
			// prev
			statPanel.add(createButton("上一页 <<", resultStartIndex - RESULTS_PER_PAGE));
			int lowerBound = resultStartIndex - RESULTS_PER_PAGE * MAX_NAV_BUTTONS / 2;
			if (lowerBound < 0)
				lowerBound = 0;
			int pageIndex = lowerBound / RESULTS_PER_PAGE + 1;
			// page
			for (int i = 0; i < MAX_NAV_BUTTONS; i++) {
				int startIndex = lowerBound + i * RESULTS_PER_PAGE;
				if (startIndex >= hitSet.size())
					break;
				statPanel.add(createButton("" + (pageIndex + i), startIndex));
			}
			// next
			statPanel.add(createButton(">> 下一页", resultStartIndex + RESULTS_PER_PAGE));
			statPanel.updateUI();
		}
	}
	
	public static String getTextFileContent(String fileName) {
		return FileUtils.getFileContent(fileName);
	}
	
	/**
	 * 生成显示在ResultPane的Html文件
	 * this implementation is absolutely extremely and definitely fucking slow, by calling
	 * so many "replace" function.
	 * however, I use it for simplification and will not cause much side-effect because
	 * there will NOT be many results on a single page. hahaha...
	 * @param parsedQuery 
	 * @param time 
	 */
	public static void generateHtmlFile(HitSet hitSet, int resultStartIndex, List<String> parsedQuery, long time) {
		StringBuilder allResults = new StringBuilder();
		List<Hit> results = hitSet.getHits(resultStartIndex, RESULTS_PER_PAGE);
		for (Hit hit : results) {
			Map<String, String> property = null; 
			try {
				property = parser.getFileProperty(hit.fileName);
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			String[] termArray = new String[parsedQuery.size()];
			parsedQuery.toArray(termArray);
			
			String single = SINGLE_RESULT_TEMPLATE;
			single = single.replace("$URL_STR$", hit.fileName);
			single = single.replace("$AUTHOR$", 
					termLabeler.twoPhaseLabel(property.get(IParser.AUTHOR_KEY), 
							termArray, termArray, true));
			single = single.replace("$AUTHOR_NICK$", 
					termLabeler.twoPhaseLabel(property.get(IParser.AUTHOR_NICK_KEY), 
							termArray, termArray, true));
			single = single.replace("$TITLE$",
					termLabeler.twoPhaseLabel(
							ViewUtils.getHTMLValidText(property.get(IParser.TITLE_KEY)), 
							termArray, termArray, true));
			single = single.replace("$FILE_SIZE$", property.get(IParser.FILE_SIZE_KEY));
			String text = ViewUtils.getHTMLValidText(property.get(IParser.CONTENT_KEY));
			text = summary.getSummary(combineQuery(parsedQuery), text);
			text = termLabeler.twoPhaseLabel(text, termArray, termArray, true);
			single = single.replace("$SUMMARY$", text);
			allResults.append(single);
		}
		String result = RESULT_TEMPLATE;
		result = result.replace("$QUERY_TEXT$", parsedQuery.toString());
		result = result.replace("$RESULT_COUNT$", "" + hitSet.size());
		result = result.replace("$QUERY_TIME$", "" + (time / 1000.0) + "s");
		result = result.replace("$BEGIN_INDEX$", "" + (resultStartIndex + 1));
		result = result.replace("$END_INDEX$", 
				"" + Math.min(resultStartIndex + RESULTS_PER_PAGE, hitSet.size()));
		result = result.replace("$RESULT_LIST$", allResults);
		try {
			FileUtils.writeStringToFile(TEMP_HTML_FILE_NAME, result);
		} catch (IOException e) {
			e.printStackTrace();
			System.out.println("Failed to create result file for query: " + parsedQuery);
		}
	}

	/**
	 * 用空格隔开，用于给Summary
	 * @return
	 */
	private static String combineQuery(List<String> parsedQuery) {
		StringBuilder sb = new StringBuilder();
		sb.append(parsedQuery.get(0));
		for (int i = 1; i < parsedQuery.size(); i++) {
			sb.append(' ');
			sb.append(parsedQuery.get(i));
		}
		return sb.toString();
	}
	
	public static void main(String[] args) {
		MainFrame gui = new MainFrame("Tiny Searcher v0.1");
		gui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		gui.setSize(800, 600);
		gui.setVisible(true);
	}
}
